// 泛型可以用于 函数、接口、类、type
// 如果在使用的时候 无法确定当时的类型，可以采用泛形来定义

class Person1 {
  constructor(public name: string, public age: number) {}
}
/* 
// 声明一个可以new 的 数据类型
// 下面两句，代码是一样的
// 但是any 的数据类型，会导致使用某些数据方法时报错，需要断言
// 如果在使用的时候 无法确定当时的类型，可以采用泛形来定义
// 泛型可以用于 函数、类、接口、type

type IClazz = new (name: string, age: number)=> any;

interface IClazz1 {
  new (name: string, age: number): any;
}

*/
interface IClazz1<T> {
  new (name: string, age: number): T;
}
// Person当传入target: IClazz1<T>位置时会自动推断数据类型，并把该数据类型传入所有<T>位置
// 所以 createInstance<T>，是非必传的

// 在声明的时候，ts还没执行，不知道是string，要断言
function createInstance<T, K>(target: IClazz1<T>, name: K, age: number) {
  return new target(name as string, age);
}
// ts 中在使用的时候确定类型 可以通过泛型(传递的是类型) TKUMN O P

const animal = createInstance<Person, string>(Person, "Cat", 18);

// 根据提供的数据生成对应长度的数组............................................................
const createArr1 = <T>(times: number, val: T): T[] => {
  const arr = [] as T[]; // 限制了一下
  for (let i = 0; i < times; i++) {
    arr.push(val);
  }
  return arr;
  // return Array.from({length:times}).fill(val)  as  T[]
};
console.log(createArr1(3, "abc"));
console.log(createArr1(3, 123));

// 写辅助函数的函数的时候可以写 多个泛型用于保存值..............................................................................
// 值的交换
// function swap<T,K>(tuple:[T,K]):[K,T]{
//     return [tuple[1],tuple[0]]
// }
// const r = swap(['jw',true])

// 提取类型
type ISwap = <T, K>(tuple: [T, K]) => [K, T]; // 此类型可以复用
let swap: ISwap = (tuple) => {
  return [tuple[1], tuple[0]];
};
let r2 = swap(["abc", 123]); //->[123,'abc']

//  ..........................................................................

// 泛型使用的时候传递类型，可以直接推导，但是内部调用的时候没有确定类型
type ICallbackk<T> = (item: T, index: number) => void;
type IforEach = <T>(arr: T[], callback: ICallbackk<T>) => void;
const forEach1: IforEach = (arr, callback) => {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i], 1);
  }
}; //这个地方没有执行啊
// IForEach<T> 表示使用接口的时候确定类型 ===> 它的提示消息是明确的 item: string | number数据类型
// <T>():void 在使用这个函数的函数传入类型===> 它的提示消息是item: T 是错误写法 泛型的使用需要能正常推到，但是内部的callback没有真正的执行，还是认为arr:T[]
forEach1([1, 2, 3, "a", "b", "c"], function (item, index) {});
//写在前面 就是表示使用类型的时候传参，写到的数的前面意着调用函数的时候传道参数

// IForEach<T> 表示使用接口的时候确定类型
// <T>():void 在使用这个函数的函数传入类型
// interface IForEach {
//     <T>(arr: T[],callback:(val:T)=>void): void
// }

// type ICallback = <T>(val: T) => void  //  错误写法 泛型的使用需要能正常推到，但是内部的callback没有真正的执行，还是认为arr:T[]
// type IForEach = <T>(arr: T[], callback: ICallback) => void
// const forEach: IForEach = (arr, callback) => {
//     for (let i = 0; i < arr.length; i++) {
//         // ts 并没有真正的执行内部的callback(arr[i])
//         callback(arr[i])
//     }
// }
type ICallback<T> = (val: T) => void; // 2
type IForEach = <T>(arr: T[], callback: ICallback<T>) => void;
const forEach: IForEach = (arr, callback) => {
  for (let i = 0; i < arr.length; i++) {
    callback(arr[i]);
  }
};
// 使用forEach才传递的类型，而不是定义接口的时候 传递类型

// string | number | {}
forEach(["A", 2, 3, {}], function (val) {
  console.log(val);
});
export {};
